﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.Net;
using System.Globalization;
using System.Security.Policy;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ProgressBar;
using System.Text.RegularExpressions;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Net.Mime.MediaTypeNames;
using System.Diagnostics;
using System.Threading;
using System.Web;


namespace PLC_Communication
{
    public partial class Form1 : Form
    {
        private string serialPortName; //用户选择的串口
        private const int WM_DeviceChange = 0x0219; //设备改变变量
        private const int DBT_DeviceArrival = 0x8000; //设备接入(usb插入)
        private const int DBT_DeviceRemoveComplete = 0x8004;//设备拔出（usb拔出）

        private byte STX;//帧头
        private byte STX_rec; //接收数据帧头
        private int CMD;//功能码
        private int ADDRESS;//起始地址
        private int BYTE;//字节数
        private byte ETX;//结束符
        private byte ETX_rec; //接收数据结束符
        private int CHECK;//校验和


        CancellationToken cancellationToken = new CancellationToken();
        private string PlcBand; //PLC品牌

        private string lastHexString;//接收到的报文
        private List<byte> receivedData = new List<byte>();

        private bool errorMessageShow;

        private volatile bool receiveWait; //标记是否收到完整报文  //线程安全性使用 volatile 关键字


        public Form1()
        {
            InitializeComponent();

            btn_readVal.Click += async (sender, e) => await PauseAndContinue();//读取单个寄存器的值

            btn_ModbusRead.Click += async (sender, e) => await modbusPauseAndContinue();//modbus模式读取

            btn_readYMstation.Click += async (sender, e) => await ReadPlcStatus();//读取PLC XY的状态
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            //查找串口号
            Find_SerialPort();
            //设置默认通讯参数
            Set_default_parameters();
            //根据PLC修改协议
            PLC_selection();

            cBoxRecHex.Checked = true;
            cbox_sendHEX.Checked = true;
        }

        public void PLC_selection()
        {
            /*如果有数据，显示第0个*/
            //cbox_PLCselect.SelectedIndex = cbox_PLCselect.Items.Count > 0 ? 0 : -1; //PLC品牌选择
            cBox_ElementType.SelectedIndex = cBox_ElementType.Items.Count > 0 ? 0 : -1;//PLC线圈开关选择
            comBox_ModbusSelect.SelectedIndex = comBox_ModbusSelect.Items.Count > 0 ? 0 : -1;//Modbus连接方式选择
            cBox_Function.SelectedIndex = cBox_Function.Items.Count > 0 ? 0 : -1;//modbus从站号选择
        }

        /*定时器刷新PLC状态*/
        private void timer1_Tick(object sender, EventArgs e)
        {

            if (cbox_PLCselect.Text == "三菱")
            {
                STX = 0x02;
                ETX = 0x03;
                STX_rec = 0x02;
                ETX_rec = 0x03;
                PlcBand = "三菱";
            }

            else if (cbox_PLCselect.Text == "台达")
            {
                STX = 0x3A;
                ETX = 0x03;
                STX_rec = 0x81;
                STX_rec = 0x03;
                PlcBand = "台达";
            }

        }




        /// <summary>
        /// 检测设备热插拔
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == WM_DeviceChange)//设备改变
            {
                /*usb串口拔出*/
                if (m.WParam.ToInt32() == DBT_DeviceRemoveComplete)
                {
                    /*清除串口Combox框数据*/
                    cob_COM.Items.Clear();

                    /*重新获取串口*/
                    Find_SerialPort();

                    /*在串口打开的情况下串口被拔出*/
                    if (btn_openCOM.Text == "串口关闭")
                    {
                        if (!serialPort1.IsOpen)//用户打开的串口被关闭，说明被热插拔的串口是当前在使用的串口
                        {
                            /*按钮变化*/
                            btn_openCOM.Text = "打开串口";
                            btn_openCOM.BackColor = Color.White;

                            /*释放原先串口资源*/
                            serialPort1.Dispose();

                            /*重新显示获取的第一个串口号*/
                            cob_COM.SelectedIndex = cob_COM.Items.Count > 0 ? 0 : -1;
                        }
                    }
                    /*用户 没有 打开串口情况下，串口被拔出*/
                    else
                    {
                        /*重新查找串口号*/
                        Find_SerialPort();
                    }
                }
                /*usb串口重新连上*/
                else if (m.WParam.ToInt32() == DBT_DeviceArrival)
                {
                    /*清除串口Combox框数据*/
                    cob_COM.Items.Clear();

                    /*重新查找串口号*/
                    Find_SerialPort();

                    /*如果用户打开过一个串口*/
                    if (btn_openCOM.Text == "打开串口")
                    {
                        /*显示用户之前打开过的串口号*/
                        cob_COM.Text = serialPortName;
                    }
                    else
                    {
                        /*重新显示获取的第一个串口号*/
                        cob_COM.SelectedIndex = cob_COM.Items.Count > 0 ? 0 : -1;
                    }
                }
            }
            base.WndProc(ref m);



        }


        /// <summary>
        /// 打开串口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_openCOM_Click(object sender, EventArgs e)
        {
            if (btn_openCOM.Text == "打开串口")
            {
                try
                {
                    /*设置按钮变化*/
                    btn_openCOM.Text = "关闭串口";
                    btn_openCOM.BackColor = Color.Green;

                    /*设置按钮功能*/
                    serialPort1.PortName = cob_COM.Text;//串口号                    
                    serialPort1.BaudRate = int.Parse(cob_baudRate.Text);//波特率
                    serialPort1.DataBits = int.Parse(cob_dataBit.Text);//数据位

                    if (cob_checkBit.Text == "偶校验") { serialPort1.Parity = Parity.Even; }//校验位
                    if (cob_checkBit.Text == "奇校验") { serialPort1.Parity = Parity.Odd; }//校验位
                    if (cob_checkBit.Text == "无校验") { serialPort1.Parity = Parity.None; }//校验位

                    if (cob_stopBit.Text == "1") { serialPort1.StopBits = StopBits.One; } //停止位
                    else if (cob_stopBit.Text == "1.5") { serialPort1.StopBits = StopBits.OnePointFive; } //停止位
                    else if (cob_stopBit.Text == "2") { serialPort1.StopBits = StopBits.Two; } //停止位

                    /*记录用户打开的串口号*/
                    serialPortName = cob_COM.Text;

                    /*打开串口*/
                    serialPort1.Open();

                    //打开计时器
                    timer1.Enabled = true;

                    string send_str = "05";
                    byte[] HexBytes = strToHexByte(send_str);
                    try
                    {
                        /*串口发送数据*/
                        serialPort1.Write(HexBytes, 0, HexBytes.Length);
                    }
                    catch
                    {
                        MessageBox.Show("输入错误");
                    }
                }
                catch
                {
                    //弹出对话框
                    MessageBox.Show("串口打开失败", "提示!");
                }
            }
            else
            {
                /*设置按钮变化*/
                btn_openCOM.Text = "打开串口";
                btn_openCOM.BackColor = Color.Transparent;

                /*设置按钮功能*/
                serialPort1.Close();
            }
        }


        /// <summary>
        /// 处理完整报文
        /// </summary>
        private void processRecivedDate()
        {
            lock (receivedData)
            {
                while (receivedData.Count > 0)
                {
                    if (PlcBand == "三菱")
                    {
                        byte rec_stx = 0x02;
                        byte rec_etx = 0x03;
                        byte rec_ack = 0x06;
                        // 检查是否有完整的报文
                        int startIndex = receivedData.IndexOf(rec_stx);
                        int endIndex = receivedData.IndexOf(rec_etx);
                        int ackIndex = receivedData.IndexOf(rec_ack);

                        if (startIndex != -1 && endIndex != -1 && endIndex > startIndex)
                        {
                            //  errorMessageShow = false;
                            byte[] completeMessage = receivedData.GetRange(startIndex, endIndex - startIndex + 1).ToArray();

                            //处理报文
                            HandleMessage(completeMessage);

                            // 移除已处理的报文
                            receivedData.RemoveRange(0, endIndex + 1);
                        }
                        else if (ackIndex != -1)
                        {
                            byte[] completeMessage = receivedData.ToArray();

                            //处理报文
                            HandleMessage(completeMessage);

                            // 移除已处理的报文
                            receivedData.RemoveRange(0, ackIndex + 1);
                        }
                        else
                        {
                            // errorMessageShow = true;
                            // 如果没有完整的报文，退出循环                       
                            break;
                        }
                    }
                    else
                    {
                        byte[] completeMessage = receivedData.ToArray();
                        //处理报文
                        HandleMessage(completeMessage);
                        receivedData.RemoveRange(0, completeMessage.Length);
                        break;
                    }
                }
            }
        }


        /// <summary>
        /// 处理接受到的报文
        /// </summary>
        /// <param name="completeMessage"></param>
        private void HandleMessage(byte[] completeMessage)
        {
            /*Byte值根据ASCII码表转换为String*/
            string answerMeg = Encoding.Default.GetString(completeMessage);

            //获取最后一次的字符串
            lastHexString = byteToHexstr(completeMessage);

            receiveWait = true;//标记 接收数据接收到新完整报文

            /***  当你在一个后台线程中执行操作（例如，通过 BackgroundWorker、Task 或 Thread），
             *  并且需要更新 UI（例如，更新文本框的内容），
             *  你必须使用 Invoke 或 BeginInvoke 方法来确保这些操作在 UI 线程上执行。 ***/

            /* Invoke 方法用于在创建控件的线程上执行委托*/
            Invoke(new Action(() =>
            {
                //获取当前时间
                DateTime currentTime = DateTime.Now;
                string timeStr = currentTime.ToString("yyyy-MM-dd HH:mm:ss" + " ");
                //点击16进制显示
                if (cBoxRecHex.Checked)
                {
                    //向当前文本框添加转成16进制字符串的数据
                    tbox_rec.AppendText(timeStr + "接受数据:  " + byteToHexstr(completeMessage) + "\r\n");//Environment.NewLine
                    tbox_rec.AppendText(Environment.NewLine);

                }
                else
                {
                    //向当前文本框添加数据
                    tbox_rec.AppendText(timeStr + "接受数据:  " + answerMeg + "\r\n");
                    tbox_rec.AppendText(Environment.NewLine);

                }
            })); //*/
        }


        private StringBuilder _receivedBuffer = new StringBuilder();
        private DateTime _lastReceivedTime;
        /// <summary>
        /// 串口接收数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            /*1.获取可以读取的字节数*/
            int len = serialPort1.BytesToRead;

            /*2.根据字节数创建缓存数据数组*/
            byte[] bufferDate = new byte[len];

            /*3.把数据读到缓存数组中*/
            serialPort1.Read(bufferDate, 0, len);

            /*确保在同一时间只有一个线程可以修改它，避免断帧*/
            lock (receivedData)
            {
                receivedData.AddRange(bufferDate);
            }


            //超时机制（适用于不定长报文）
            // 假设超时时间为 100ms
            _receivedBuffer.Append(serialPort1.ReadExisting());
            _lastReceivedTime = DateTime.Now;
            Task.Delay(100).ContinueWith(_ =>
            {
                if ((DateTime.Now - _lastReceivedTime).TotalMilliseconds >= 100)
                {
                    //处理完成报文
                    processRecivedDate();
                    _receivedBuffer.Clear();
                    lastHexString = byteToHexstr(bufferDate);
                }
            });

            if (errorMessageShow)
            {
                Invoke(new Action(() =>
                {
                    //获取当前时间
                    DateTime currentTime = DateTime.Now;
                    string timeStr = currentTime.ToString("yyyy-MM-dd HH:mm:ss" + " ");
                    //点击16进制显示 
                    if (cBoxRecHex.Checked)
                    {
                        //向当前文本框添加转成16进制字符串的数据
                        tbox_rec.AppendText(timeStr + "接受数据:  " + byteToHexstr(bufferDate) + "\r\n");//Environment.NewLine
                        tbox_rec.AppendText(Environment.NewLine);
                    }
                    else
                    {
                        //向当前文本框添加数据
                        //  tbox_rec.AppendText(timeStr + "接受数据:  " + answerMeg + "\r\n");
                        tbox_rec.AppendText(Environment.NewLine);
                    }


                }));
            }

        }

        /// <summary>
        /// 接收文本框 清空
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_clearRecivel_Click(object sender, EventArgs e)
        {
            tbox_rec.Clear();
        }



        /// <summary>
        ///  查找串口号
        /// </summary>
        public void Find_SerialPort()
        {
            /*获取电脑上可用串口号*/
            string[] ports = SerialPort.GetPortNames();

            /*串口号添加到Combox中*/
            cob_COM.Items.AddRange(ports);

            /*如果有数据，显示第0个*/
            cob_COM.SelectedIndex = cob_COM.Items.Count > 0 ? 0 : -1;
        }


        /// <summary>
        /// 设置默认通讯参数
        /// </summary>
        public void Set_default_parameters()
        {
            cob_baudRate.Text = "9600";//波特率
            cob_dataBit.Text = "8";//数据位
            cob_stopBit.Text = "1";   //停止位
            cob_checkBit.Text = "偶校验";//校验位
        }


        /// <summary>
        /// 字节组转16进制字符串显示
        /// </summary>
        /// /*"X2" 格式说明符： "X" 表示将数字格式化为十六进制。 "2" 表示输出两位数字。如果字节值小于 16（即小于 0x10），则在前面补 0 以确保两位输出。例如，字节值 10（十进制）会被格式化为 "0A"。*/
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string byteToHexstr(byte[] bytes)
        {
            string returnStr = "";

            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2");
                    returnStr += "  ";
                }
            }
            return returnStr;
        }

        /// <summary>
        /// 点击发送数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_send_Click(object sender, EventArgs e)
        {
            string send_str = tbox_send.Text.ToString();

            //获取当前时间
            DateTime currentTime = DateTime.Now;
            string timeStr = currentTime.ToString("yyyy-MM-dd HH:mm:ss" + " ");

            try
            {
                if (send_str.Length > 0)
                {
                    /*转成16进制发送*/
                    if (cbox_sendHEX.Checked)
                    {
                        byte[] HexBytes = strToHexByte(send_str);
                        try
                        {
                            /*串口发送数据*/
                            serialPort1.Write(HexBytes, 0, HexBytes.Length);
                        }
                        catch
                        {
                            MessageBox.Show("输入错误");
                        }


                        /*向当前文本框添加发送的数据*/
                        tbox_rec.AppendText(timeStr + "发送数据:  " + tbox_send.Text + "\r\n");

                    }
                    /*10进制发送*/
                    else
                    {
                        /*串口发送数据*/
                        serialPort1.Write(send_str);

                        /*向当前文本框添加发送的数据*/
                        tbox_rec.AppendText(timeStr + "发送数据:  " + tbox_send.Text + "\r\n");
                    }
                }
            }
            catch (Exception) { }
        }

        /// <summary>
        /// 字符串转16进制 
        /// </summary>
        /// <param name="hexString"></param>
        /// <returns></returns>
        public static byte[] strToHexByte(string hexString)
        {
            int i;
            /*清除空格*/
            hexString = hexString.Replace(" ", "");

            /*如果是偶数个*/
            if ((hexString.Length % 2) == 0)
            {
                /*两个字节组成一个16进制*/
                byte[] returnBytes = new byte[hexString.Length / 2];
                try
                {
                    for (i = 0; i < hexString.Length / 2; i++)
                    {
                        /*每次提取字符串的两个字符，转换成16进制，存储到 返回数组 中*/
                        returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
                    }
                }
                catch
                {
                    MessageBox.Show("含有非16进制字符", "提示");
                    return null;
                }
                return returnBytes;
            }
            /*如果是奇数个*/
            else
            {
                byte[] returnBytes = new byte[(hexString.Length + 1) / 2];
                try
                {
                    for (i = 0; i < (hexString.Length - 1) / 2; i++)
                    {
                        returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
                    }
                    returnBytes[returnBytes.Length - 1] = Convert.ToByte(hexString.Substring(hexString.Length - 1, 1).PadLeft(2, '0'), 16);
                }
                catch
                {
                    MessageBox.Show("含有非16进制字符", "提示");
                    return null;
                }
                return returnBytes;
            }

        }

        //清除发送文本
        private void btn_clearSend_Click(object sender, EventArgs e)
        {
            tbox_send.Clear();
        }




        /// <summary>
        ///  三菱协议强制元件ON/OFF
        /// </summary>
        /// <param name="CMD"></param>
        /// <param name="Address"></param>
        public void Mitsubishi_ForceON_OFF(byte CMD, byte[] Address)
        {
            byte[] complete_Message = { STX, CMD, Address[0], Address[1], Address[2], Address[3], ETX };

            /*计算检验和*/
            int sum = 0;
            for (int i = 1; i < complete_Message.Length; i++)
            {
                sum += complete_Message[i];
            }

            // 将整数转换为两位十六进制格式的字符串
            string sum_HexString = sum.ToString("X2");

            //只保留最后两位
            string lastTwodigits = sum_HexString.Substring(sum_HexString.Length - 2);

            //拆开,将字符串分解为字符数组
            char[] characters = lastTwodigits.ToCharArray();

            //拆开的字符装成ASCII码
            string checkSum_Ascii_1 = ((int)characters[0]).ToString("X2");
            string checkSum_Ascii_2 = ((int)characters[1]).ToString("X2");


            /*10进制转成16进制放到发送的数据中*/
            byte checkSum_Byte_1 = Convert.ToByte(checkSum_Ascii_1, 16);
            byte checkSum_Byte_2 = Convert.ToByte(checkSum_Ascii_2, 16);

            byte[] sendDate = { STX, CMD, Address[0], Address[1], Address[2], Address[3], ETX, checkSum_Byte_1, checkSum_Byte_2 };

            //串口发送数据
            serialPort1.Write(sendDate, 0, sendDate.Length);
        }



        /// <summary>
        /// /*计算检验和*/
        /// </summary>
        /// <param name="sendDate"></param>
        /// <param name="checkSum_Byte_1"></param>
        /// <param name="checkSum_Byte_2"></param>
        public void CompleteChecksum(byte[] halfDate, ref byte checkSum_Byte_1, ref byte checkSum_Byte_2)
        {
            int Sum = 0;
            int endIndex = 0;
            /*算到0x03停止相加*/

            //1.查找0x03的位置
            byte targeValue = ETX;
            for (int i = 0; i < halfDate.Length; i++)
            {
                if (halfDate[i] == targeValue)
                {
                    endIndex = i;
                    break;
                }
            }

            for (int i = 1; i < endIndex + 1; i++)
            {
                Sum += halfDate[i];
            }
            //2. 将整数转换为两位十六进制格式的字符串
            string sum_HexString = Sum.ToString("X2");

            //3.只保留最后两位
            string lastTwodigits = sum_HexString.Substring(sum_HexString.Length - 2);

            //4.拆开,将字符串分解为字符数组
            char[] characters = lastTwodigits.ToCharArray();

            //5.拆开的字符装成ASCII码
            string checkSum_Ascii_1 = ((int)characters[0]).ToString("X2");
            string checkSum_Ascii_2 = ((int)characters[1]).ToString("X2");

            //6.ASCII整数值转成16进制
            checkSum_Byte_1 = Convert.ToByte(checkSum_Ascii_1, 16);
            checkSum_Byte_2 = Convert.ToByte(checkSum_Ascii_2, 16);

        }




        /// <summary>
        /// 点击使得Y,M状态为OFF
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_YM_ON_Click(object sender, EventArgs e)
        {
            if (PlcBand == "三菱")
            {
                if (cBox_ElementType.Text == "Y")
                {
                    byte CMD = 0x37;

                    byte[] Address_Byte = new byte[4];
                    int number_Y = Convert.ToInt32(numericUpDown_YMaddress.Value);

                    Address_Translation_Force_Y(number_Y, ref Address_Byte);//es:Y7  -> 48 55 48 55

                    Mitsubishi_ForceON_OFF(CMD, Address_Byte);
                }
                else if (cBox_ElementType.Text == "M")
                {
                    byte CMD = 0x37;

                    byte[] Address_Byte = new byte[4];
                    int number_M = Convert.ToInt32(numericUpDown_YMaddress.Value);

                    Address_Translation_Force_M(number_M, ref Address_Byte);//es:M7  -> 48 56 48 55

                    Mitsubishi_ForceON_OFF(CMD, Address_Byte);
                }
            }
            else if (PlcBand == "台达")
            {

            }
            else if (PlcBand == "Modbus-rtu")
            {

            }

        }

        /// <summary>
        /// 点击使得Y,M状态为OFF
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_YM_OFF_Click(object sender, EventArgs e)
        {
            if (PlcBand == "三菱")
            {
                if (cBox_ElementType.Text == "Y")
                {
                    byte CMD = 0x38;

                    byte[] Address_Byte = new byte[4];
                    int number_Y = Convert.ToInt32(numericUpDown_YMaddress.Value);

                    Address_Translation_Force_Y(number_Y, ref Address_Byte);//es:Y7  -> 48 55 48 53

                    Mitsubishi_ForceON_OFF(CMD, Address_Byte);
                }
                else if (cBox_ElementType.Text == "M")
                {
                    byte CMD = 0x38;

                    byte[] Address_Byte = new byte[4];
                    int number_M = Convert.ToInt32(numericUpDown_YMaddress.Value);

                    Address_Translation_Force_M(number_M, ref Address_Byte);//es:M7  -> 48 56 48 55

                    Mitsubishi_ForceON_OFF(CMD, Address_Byte);
                }
            }
            else if (PlcBand == "台达")
            {

            }
        }

        /// <summary>
        /// 地址转换 例如：Y7 -> 0507
        /// </summary>
        /// <param name="Y_Number_Val"></param>
        /// <param name="Address_Byte"></param>
        private void Address_Translation_Force_Y(int Y_Number_Val, ref byte[] Address_Byte)
        {
            int y0 = 0x0500;
            int Address_Int = 0;
            List<int> Y_address = new List<int>();
            if (Y_Number_Val < 8)
            {
                for (int i = 0; i < 8; i++)
                {
                    Y_address.Add(y0 + i);
                }
            }
            else if (Y_Number_Val < 18 && Y_Number_Val > 9)
            {
                for (int i = 10; i < 18; i++)
                {
                    Y_address.Add(y0 + i);
                }
            }
            else if (Y_Number_Val < 28 && Y_Number_Val > 19)
            {
                for (int i = 20; i < 28; i++)
                {
                    Y_address.Add(y0 + i);
                }
            }
            else if (Y_Number_Val < 38 && Y_Number_Val > 29)
            {
                for (int i = 30; i < 38; i++)
                {
                    Y_address.Add(y0 + i);
                }
            }
            else
            {
                MessageBox.Show("地址输入错误", "提示！");
            }

            Address_Int = Y_address[Y_Number_Val];

            string hexString = Address_Int.ToString("X4");
            string rearranged = hexString.Substring(2, 2) + hexString.Substring(0, 2);//0507 -> 0705

            for (int i = 0; i < rearranged.Length; i++)
            {
                Address_Byte[i] = (byte)rearranged[i];
            }
        }



        /// <summary>
        /// 地址转换 例如：M7 -> 0807
        /// </summary>
        /// <param name="M_Number_Val"></param>
        /// <param name="Address_Byte"></param>
        private void Address_Translation_Force_M(int M_Number_Val, ref byte[] Address_Byte)
        {
            int m0 = 2048;
            int increment = M_Number_Val;
            int Address_Int = m0 + increment;
            string hexString = Address_Int.ToString("X4");
            string rearranged = hexString.Substring(2, 2) + hexString.Substring(0, 2);//0507 -> 0705

            for (int i = 0; i < rearranged.Length; i++)
            {
                Address_Byte[i] = (byte)rearranged[i];
            }
        }


        /// <summary>
        /// 地址转换寄存器地址 例如：D123 = 10F6
        /// </summary>
        /// <param name="destination_Address_val"></param>
        private void Address_Translation_D(int destination_Address_val, ref byte[] destination_Address_byte)
        {
            //计算寄存器地址
            // 定义十六进制字符串并转换为十进制
            int D0 = Convert.ToInt32("1000", 16);  // D0 = 1000H = 4096 , D10 = 4116          

            int increment = 2;//线性增量 (d10-d0)/10 =  (4116-4096)/10 

            int destination_Address_Dec = D0 + (destination_Address_val - 0) * increment;//目标地址10进制

            string destination_Address_Str = destination_Address_Dec.ToString("X2"); //将整数转换为十六进制字符串 es:D10 -> "1014"

            destination_Address_byte = new byte[destination_Address_Str.Length];  // 创建一个字节数组来存储结果

            for (int i = 0; i < destination_Address_Str.Length; i++)
            {
                // 将字符转换为十六进制整数
                destination_Address_byte[i] = (byte)destination_Address_Str[i]; //每个字符的ASCII值 es:"1014"-> 49 48 49 52 
            }
        }


        /// <summary>
        /// 16进制字符转ASII码 es:"1014" ->49 48 49 52
        /// </summary>
        public void HexadecimalToASCiiBytes(string hex, ref byte[] asciiBytes)
        {
            asciiBytes = new byte[hex.Length];

            for (int i = 0; i < asciiBytes.Length; i++)
            {
                asciiBytes[i] = (byte)hex[i];
            }
        }



        /// <summary>
        /// 异步操作读取三菱单个寄存器的值
        /// </summary>
        /// <returns></returns>
        private async Task PauseAndContinue()
        {
            receiveWait = false;//标记是否收到完整报文

            /*三菱协议初始赋值*/
            byte stx = 0x02;//帧头
            byte etx = 0x03;//帧尾
            byte cmd = 0x30;//功能码

            /*数据地址*/
            int destination_Address_val = Convert.ToInt32(numbox_registerAdd.Value);//起始数据地址D0
            byte[] destination_Address = new byte[20];
            Address_Translation_D(destination_Address_val, ref destination_Address);//起始数据地址16进制 1014 -> 49 48 49 52

            /*欲读取的数据长度 Byte数*/
            string ByteLength_str = "02";
            byte[] ByteLength_ascii = new byte[2];
            HexadecimalToASCiiBytes(ByteLength_str, ref ByteLength_ascii);

            /*检验码*/
            byte[] halfDate = { stx, cmd, destination_Address[0], destination_Address[1], destination_Address[2], destination_Address[3], ByteLength_ascii[0], ByteLength_ascii[1], etx };
            byte checkSum_Byte_1 = 0; //检验码1
            byte checkSum_Byte_2 = 0; //检验码2
            CompleteChecksum(halfDate, ref checkSum_Byte_1, ref checkSum_Byte_2);

            /*发送完整报文*/
            byte[] Complete_Message = { stx, cmd, destination_Address[0], destination_Address[1], destination_Address[2], destination_Address[3], ByteLength_ascii[0], ByteLength_ascii[1], etx, checkSum_Byte_1, checkSum_Byte_2 };

            /*串口发送*/
            try
            {
                serialPort1.Write(Complete_Message, 0, Complete_Message.Length);

                ReceiveMessageWait(cancellationToken);

                /*去除空格*/
                string AnswerMessage = lastHexString.Replace(" ", "");

                /*裁切出数据内容*/
                if (AnswerMessage.Length > 9)
                {
                    string data_Content = AnswerMessage.Substring(2, 8);

                    //重新排列字符串 后两位移动到前两位
                    string rearrangedStr = data_Content.Substring(4, 4) + data_Content.Substring(0, 4);

                    // 字节数组的长度是字符串长度的一半
                    byte[] bytes = new byte[rearrangedStr.Length / 2];

                    for (int i = 0; i < rearrangedStr.Length; i += 2)
                    {
                        //取两个字符并将其解析为一个字节
                        string hexPair = rearrangedStr.Substring(i, 2);
                        bytes[i / 2] = Convert.ToByte(hexPair, 16);
                    }

                    // 创建一个字符数组来存储转换后的字符
                    char[] chars = new char[bytes.Length];

                    // 将每个字节转换为对应的字符
                    for (int i = 0; i < bytes.Length; i++)
                    {
                        chars[i] = (char)bytes[i];
                    }

                    //组合成一个字符串
                    string result1 = new string(chars);

                    // 将字符串解析为整数
                    int number;

                    // 使用 int.TryParse 和 NumberStyles.HexNumber 解析十六进制字符串
                    // NumberStyles.HexNumber：这个枚举值告诉 TryParse 方法将输入字符串解释为十六进制数。
                    // CultureInfo.InvariantCulture：用于指定不依赖于特定文化的信息解析方式。对于十六进制解析，这通常不影响结果，但使用 InvariantCulture 是一种良好的习惯，确保解析行为一致。通过这种方式，你可以正确地将十六进制字符串转换为整数。

                    bool Output_result = int.TryParse(result1, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out number);

                    if (Output_result)
                    {
                        // 将整数转换为十六进制字符串
                        string hexStr = number.ToString("X2");

                        //将十六进制字符串转换为10进制整数
                        int decimalValue = Convert.ToInt32(hexStr, 16);

                        tbox_registerVal.Clear();
                        //赋值到文本框中
                        tbox_registerVal.Text = decimalValue.ToString();

                    }
                    else
                    { }
                }
                else
                {
                    tbox_registerVal.Text = "null";
                }
            }
            catch
            {
                MessageBox.Show("串口未打开", "提示");
            }

        }


        /// <summary>
        /// 读取单个寄存器的值
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_readVal_Click(object sender, EventArgs e) { }





        /// <summary>
        /// 写入寄存器的值
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_writeVal_Click(object sender, EventArgs e) //es: 写入D10值为30000 |  02 31 31 30 31 34 30 32 33 30 37 35 03 32 42 
        {
            receiveWait = false;//标记是否收到完整报文

            /*三菱协议初始赋值*/
            byte stx = 0x02;//帧头
            byte etx = 0x03;//帧尾
            byte cmd = 0x31;//功能码

            /*寄存器的地址*/
            int register_Address_val = Convert.ToInt32(numbox_registerAdd.Value);//起始数据地址D0
            byte[] register_Address_byte = new byte[4];
            Address_Translation_D(register_Address_val, ref register_Address_byte);

            /*Byte数*/
            string ByteLength_str = "02";
            byte[] ByteLength_ascii = new byte[2];
            HexadecimalToASCiiBytes(ByteLength_str, ref ByteLength_ascii);

            /*欲要写入的值处理*/
            byte[] rearrangeHexValue_Hex = new byte[4];
            Register_WriteValue_processing(ref rearrangeHexValue_Hex);

            /*检验码*/
            byte[] halfDate = { stx, cmd, register_Address_byte[0], register_Address_byte[1], register_Address_byte[2], register_Address_byte[3], ByteLength_ascii[0], ByteLength_ascii[1], rearrangeHexValue_Hex[0], rearrangeHexValue_Hex[1], rearrangeHexValue_Hex[2], rearrangeHexValue_Hex[3], etx };
            byte checkSum_Byte_1 = 0; //检验码1
            byte checkSum_Byte_2 = 0; //检验码2
            CompleteChecksum(halfDate, ref checkSum_Byte_1, ref checkSum_Byte_2);

            /*完整报文*/
            byte[] Complete_Message = { stx, cmd, register_Address_byte[0], register_Address_byte[1], register_Address_byte[2], register_Address_byte[3], ByteLength_ascii[0], ByteLength_ascii[1], rearrangeHexValue_Hex[0], rearrangeHexValue_Hex[1], rearrangeHexValue_Hex[2], rearrangeHexValue_Hex[3], etx, checkSum_Byte_1, checkSum_Byte_2 };

            /*串口发送*/
            serialPort1.Write(Complete_Message, 0, Complete_Message.Length);

            //等待回复完整报文
            ReceiveMessageWait(cancellationToken);

            /*检测是否写入成功*/
            string AnswerMessage = lastHexString.Replace(" ", "");

            if (AnswerMessage == "06")
            {
                MessageBox.Show("写入成功");
            }
            else { }
        }



        /// <summary>
        /// 寄存器写入值处理
        /// </summary>
        public void Register_WriteValue_processing(ref byte[] rearrangeHexValue_Hex)
        {
            string Input_Value_str = tbox_registerVal.Text;
            // 验证输入是否为空或无效

            if (string.IsNullOrEmpty(Input_Value_str))
            {
                Console.WriteLine("输入错误.");
                return;
            }

            // 将字符串转换为整数
            int writeValue = Convert.ToInt32(Input_Value_str);   //30000 -> 7530H

            // 将整数转换为十六进制字符串
            string hexValueStr = writeValue.ToString("X2");

            // 检查字符串长度是否足够进行重新排列
            if (hexValueStr.Length < 4)
            {
                hexValueStr = hexValueStr.PadLeft(4, '0'); // 如果长度不足4，左侧填充0
            }

            // 重新排列十六进制字符串
            string rearrangeHexValue_Str = hexValueStr.Substring(2, 2) + hexValueStr.Substring(0, 2);  //重新排列   "7530" -> "3075"
            rearrangeHexValue_Hex = new byte[4];
            HexadecimalToASCiiBytes(rearrangeHexValue_Str, ref rearrangeHexValue_Hex);
        }



        /// <summary>
        /// 选择PLC类型时
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cbox_PLCselect_SelectedIndexChanged(object sender, EventArgs e)
        {
            string send_str = "05";
            byte[] HexBytes = strToHexByte(send_str);
            try
            {
                /*串口发送数据*/
                serialPort1.Write(HexBytes, 0, HexBytes.Length);
            }
            catch
            { }
        }



        /// <summary>
        /// Y的地址表转换
        /// </summary>
        private void Address_Translation_Y(int Y_Origin_address, ref byte[] Address_Byte)
        {
            int Y_number = 0;
            if (-1 < Y_Origin_address && Y_Origin_address < 8)
            {
                Y_number = 0;
            }
            else if (Y_Origin_address < 18)
            {
                Y_number = 1;
            }
            else if (Y_Origin_address < 28)
            {
                Y_number = 2;
            }
            else if (Y_Origin_address < 38)
            {
                Y_number = 3;
            }
            else
            {

            }

            int[] Y_Address = new int[100];   // 0x00A0; //160
            for (int i = 0; i < Y_Address.Length; i++)
            {
                Y_Address[i] = 160 + i;
            }

            // 将十进制整数转换为十六进制字符串
            string hexString = Y_Address[Y_number].ToString("X4");

            for (int i = 0; i < hexString.Length; i++)
            {
                Address_Byte[i] = (byte)hexString[i];
            }

        }


        /// <summary>
        /// 读取XY当前的状态
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_readYMstation_Click(object sender, EventArgs e) { }

        private async Task ReadPlcStatus()
        {
            readXYstation();
        }

        /// <summary>
        /// 读取XY当前的状态
        /// </summary>
        private void readXYstation()
        {
            /*三菱协议初始赋值*/
            byte stx = 0x02;//帧头
            byte etx = 0x03;//帧尾
            byte cmd = 0x30;//功能码

            //起始地址转换
            int Y_Origin_address = 0; //起始地址
            byte[] Address_Byte = new byte[4];
            Address_Translation_Y(Y_Origin_address, ref Address_Byte);


            /*欲读取的数据长度 Byte数*/
            string ByteLength_str = "01";
            byte[] ByteLength_ascii = new byte[2];
            HexadecimalToASCiiBytes(ByteLength_str, ref ByteLength_ascii);

            /*检验码*/
            byte[] halfDate = { stx, cmd, Address_Byte[0], Address_Byte[1], Address_Byte[2], Address_Byte[3], ByteLength_ascii[0], ByteLength_ascii[1], etx };
            byte checkSum_Byte_1 = 0; //检验码1
            byte checkSum_Byte_2 = 0; //检验码2
            CompleteChecksum(halfDate, ref checkSum_Byte_1, ref checkSum_Byte_2);

            /*发送完整报文*/
            byte[] Complete_Message = { stx, cmd, Address_Byte[0], Address_Byte[1], Address_Byte[2], Address_Byte[3], ByteLength_ascii[0], ByteLength_ascii[1], etx, checkSum_Byte_1, checkSum_Byte_2 };

            /*标记是否收到完整报文，等待新完整报文*/
            receiveWait = false;

            /*串口发送*/
            serialPort1.Write(Complete_Message, 0, Complete_Message.Length);

            //等待完整报文回复
            ReceiveMessageWait(cancellationToken);

            /*去除空格*/
            string AnswerMessage = lastHexString.Replace(" ", "");

            /*裁切出数据内容*/
            if (AnswerMessage.Length >= 8)
            {
                string data_Content = AnswerMessage.Substring(2, 4);

                //重新排列字符串 后两位移动到前两位
                //string rearrangedStr = data_Content.Substring(2, 2) + data_Content.Substring(0, 2);
                string rearrangedStr = data_Content;

                // 字节数组的长度是字符串长度的一半
                byte[] bytes = new byte[rearrangedStr.Length / 2];

                for (int i = 0; i < rearrangedStr.Length; i += 2)
                {
                    //取两个字符并将其解析为一个字节
                    string hexPair = rearrangedStr.Substring(i, 2);
                    bytes[i / 2] = Convert.ToByte(hexPair, 16);
                }

                // 创建一个字符数组来存储转换后的字符
                char[] chars = new char[bytes.Length];

                // 将每个字节转换为对应的字符
                for (int i = 0; i < bytes.Length; i++)
                {
                    chars[i] = (char)bytes[i];
                }

                //组合成一个字符串
                string result1 = new string(chars);



                // 将字符串解析为整数
                int number;
                // 使用 int.TryParse 和 NumberStyles.HexNumber 解析十六进制字符串
                // NumberStyles.HexNumber：这个枚举值告诉 TryParse 方法将输入字符串解释为十六进制数。
                // CultureInfo.InvariantCulture：用于指定不依赖于特定文化的信息解析方式。对于十六进制解析，这通常不影响结果，但使用 InvariantCulture 是一种良好的习惯，确保解析行为一致。通过这种方式，你可以正确地将十六进制字符串转换为整数。

                bool Output_result = int.TryParse(result1, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out number);

                if (Output_result)
                {
                    // 将整数转换为十六进制字符串
                    string hexStr = number.ToString("X2");

                    //将十六进制字符串转换为10进制整数
                    int modsValue = Convert.ToInt32(hexStr, 16);

                    int num = modsValue; // 要转换的整数

                    // 将整数转换为二进制字符串，并填充到8位
                    string BINString = Convert.ToString(number, 2).PadLeft(8, '0');


                    //倒序排列
                    char[] c = new char[BINString.Length];

                    for (int i = 0; i < c.Length; i++)
                    {
                        c[i] = BINString[c.Length - 1 - i];
                    }


                    UpdateTextBoxColors(c);
                    /*

                    System.Windows.Forms.TextBox tBox_y0 = new System.Windows.Forms.TextBox();
                    System.Windows.Forms.TextBox tBox_y1 = new System.Windows.Forms.TextBox();
                    System.Windows.Forms.TextBox tBox_y2 = new System.Windows.Forms.TextBox();
                    //将控件放入一个数组中

                    System.Windows.Forms.TextBox[] textBoxes = { tBox_y0, tBox_y1, tBox_y2 };
                    //调用方法更新颜色
                    UpdateTextBoxColors(c, textBoxes);
                    */




                }
            }

        }




        /// <summary>
        ///   //调用方法更新颜色
        /// </summary>
        /// <param name="c"></param>
        /// <param name="textBoxes"></param>
        public void UpdateTextBoxColors(char[] c)
        {
            /*
            for (int i = 0; i < textBoxes.Length && i < c.Length; i++)
            {
                textBoxes[i].BackColor = (c[i] == '1') ? Color.Green : Color.White;
            }
            */
            //0
            if (c[0] == '1')
            {
                tBox_y0.BackColor = Color.Green;
            }
            else
            {
                tBox_y0.BackColor = Color.White;
            }

            //1
            if (c[1] == '1')
            {
                tBox_y1.BackColor = Color.Green;
            }
            else
            {
                tBox_y1.BackColor = Color.White;
            }

            //2
            if (c[2] == '1')
            {
                tBox_y2.BackColor = Color.Green;
            }
            else
            {
                tBox_y2.BackColor = Color.White;
            }

            //3
            if (c[3] == '1')
            {
                tBox_y3.BackColor = Color.Green;
            }
            else
            {
                tBox_y3.BackColor = Color.White;
            }

            //4
            if (c[4] == '1')
            {
                tBox_y4.BackColor = Color.Green;
            }
            else
            {
                tBox_y4.BackColor = Color.White;
            }

            //5
            if (c[5] == '1')
            {
                tBox_y5.BackColor = Color.Green;
            }
            else
            {
                tBox_y5.BackColor = Color.White;
            }

            //6
            if (c[6] == '1')
            {
                tBox_y6.BackColor = Color.Green;
            }
            else
            {
                tBox_y6.BackColor = Color.White;
            }

            //7
            if (c[7] == '1')
            {
                tBox_y7.BackColor = Color.Green;
            }
            else
            {
                tBox_y7.BackColor = Color.White;
            }
        }

        /// <summary>
        /// 接收读取X0-7,Y0-7的状态
        /// </summary>
        /// <param name="lastHexString1"></param>
        public void receiveTest(string lastHexString1)
        {
            /*去除空格*/
            string AnswerMessage = lastHexString1.Replace(" ", "");

            /*裁切出数据内容*/
            if (AnswerMessage.Length >= 10)
            {
                string data_Content = AnswerMessage.Substring(2, 4);

                //重新排列字符串 后两位移动到前两位
                //string rearrangedStr = data_Content.Substring(2, 2) + data_Content.Substring(0, 2);
                string rearrangedStr = data_Content;

                //   rearrangedStr = ReplaceLettersWithThree(rearrangedStr);

                // 字节数组的长度是字符串长度的一半
                byte[] bytes = new byte[rearrangedStr.Length / 2];

                for (int i = 0; i < rearrangedStr.Length; i += 2)
                {
                    //取两个字符并将其解析为一个字节
                    string hexPair = rearrangedStr.Substring(i, 2);
                    bytes[i / 2] = Convert.ToByte(hexPair, 16);
                }

                // 创建一个字符数组来存储转换后的字符
                char[] chars = new char[bytes.Length];

                // 将每个字节转换为对应的字符
                for (int i = 0; i < bytes.Length; i++)
                {
                    chars[i] = (char)bytes[i];
                }

                //组合成一个字符串
                string result1 = new string(chars);



                // 将字符串解析为整数
                int number;
                // 使用 int.TryParse 和 NumberStyles.HexNumber 解析十六进制字符串
                // NumberStyles.HexNumber：这个枚举值告诉 TryParse 方法将输入字符串解释为十六进制数。
                // CultureInfo.InvariantCulture：用于指定不依赖于特定文化的信息解析方式。对于十六进制解析，这通常不影响结果，但使用 InvariantCulture 是一种良好的习惯，确保解析行为一致。通过这种方式，你可以正确地将十六进制字符串转换为整数。

                bool Output_result = int.TryParse(result1, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out number);

                if (Output_result)
                {
                    // 将整数转换为十六进制字符串
                    string hexStr = number.ToString("X2");

                    //将十六进制字符串转换为10进制整数
                    int modsValue = Convert.ToInt32(hexStr, 16);

                    int num = modsValue; // 要转换的整数

                    // 将整数转换为二进制字符串，并填充到8位

                    string BINString = Convert.ToString(number, 2).PadLeft(8, '0');


                    //倒序排列
                    char[] c = new char[BINString.Length];

                    for (int i = 0; i < c.Length; i++)
                    {
                        c[i] = BINString[c.Length - 1 - i];
                    }


                    UpdateTextBoxColors(c);
                }

            }
        }

        /// <summary>
        /// 接收数据时候等待完整报文，等待接收消息的超时控制方法
        /// </summary>
        public void ReceiveMessageWait(CancellationToken cancellationToken)
        {
            // 增加 CancellationToken 支持，允许外部强制取消等待

            //创建 Stopwatch 对象
            //Stopwatch 是 .NET 提供的高精度计时器，用于测量时间间隔（精度可达微秒级）。
            Stopwatch sw = Stopwatch.StartNew(); // 直接启动计时器

            while (!cancellationToken.IsCancellationRequested)
            {
                //检查是否已收到完整报文
                if (receiveWait)// 如果收到完整报文  
                {
                    break;
                }

                //未收到报文时的处理                                  
                if (sw.ElapsedMilliseconds > 2000) // 如果超时（2秒）
                {
                    MessageBox.Show("读取超时");
                    //计时器清零
                    sw.Reset();// 重置计时器
                    break;

                }

                // 每次循环暂停 10 毫秒，显著降低 CPU 占用率，
                // 加入一个短暂的暂停，避免 CPU 占用过高
                Thread.Sleep(10);
            }
        }


        /// <summary>
        /// modbus更换连接方式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comBox_ModbusSelect_RightToLeftChanged(object sender, EventArgs e)
        {
            if (comBox_ModbusSelect.SelectedIndex == 1)
            {
                gBox_TcpIp.Visible = true;
            }
            else
            {
                gBox_TcpIp.Visible = false;
            }
        }




        /// <summary>
        /// modbus读取异步方法
        /// </summary>
        /// <returns></returns>
        private async Task modbusPauseAndContinue()
        {
            try
            {
                receiveWait = false; //标记是否收到完整报文

            //报文格式                    从站地址（HEX） +  功能码（）+ 寄存器起始地址+ 读寄存器个数 +CRC检验码
            //读15站号的地址2的数据             0F        +      03   +      00 02   + 00 01       + 24 E4

            //1.从站号
            int slaveStationID_Int = Convert.ToInt32(numbox_modbusID.Value);
            string slaveStationID_Str = slaveStationID_Int.ToString("X2");//将整数转换为十六进制字符串
            byte slaveStationID_Byte = Convert.ToByte(slaveStationID_Str, 16);

            //2.功能码
            int cmd_Int = cBox_Function.SelectedIndex + 1;
            int modbusAreaCode = 0;

            if (cmd_Int == 3) { modbusAreaCode = 4; }
            else if (cmd_Int == 4) { modbusAreaCode = 3; }
            string cmd_Str = cmd_Int.ToString("X2");
            byte cmd_Byte = Convert.ToByte(cmd_Str, 16);

            //3.寄存器起始地址
            int regStartAddress_Int = Convert.ToInt32(num_ModbusRegAddress.Value);
            string regStartAddress_Str = regStartAddress_Int.ToString("X4");
            byte[] regStartAddress_Byte = new byte[2];
            regStartAddress_Byte[0] = Convert.ToByte(regStartAddress_Str.Substring(0, 2), 16);
            regStartAddress_Byte[1] = Convert.ToByte(regStartAddress_Str.Substring(2, 2), 16);

            //4.寄存器个数
            int registerLength_Int = Convert.ToInt32(num_ModbusRegLen.Value);
            string registerLength_Str = registerLength_Int.ToString("X4");
            byte[] registerLength_Byte = new byte[2];
            registerLength_Byte[0] = Convert.ToByte(registerLength_Str.Substring(0, 2), 16);
            registerLength_Byte[1] = Convert.ToByte(registerLength_Str.Substring(2, 2), 16);

            //5.CRC检验码
            byte[] CalculationData = new byte[6];
            for (int i = 0; i < 6; i++)
            {
                switch (i)
                {
                    case 0:
                        CalculationData[i] = slaveStationID_Byte;
                        break;
                    case 1:
                        CalculationData[i] = cmd_Byte;
                        break;
                    case 2:
                        CalculationData[i] = regStartAddress_Byte[0];
                        break;
                    case 3:
                        CalculationData[i] = regStartAddress_Byte[1];
                        break;
                    case 4:
                        CalculationData[i] = registerLength_Byte[0];
                        break;
                    case 5:
                        CalculationData[i] = registerLength_Byte[1];
                        break;
                    default:
                        break;
                }
            }

            ushort crc_Ushort = CalculateModbusCrc(CalculationData);
            string crc_Str = crc_Ushort.ToString("X4");
            byte[] crc_Byte = new byte[2];

            //Modbus协议要求将CRC校验码以小端字节序（低字节在前）附加到报文末尾
            crc_Byte[0] = Convert.ToByte(crc_Str.Substring(2, 2), 16);
            crc_Byte[1] = Convert.ToByte(crc_Str.Substring(0, 2), 16);

            int messageLenth = CalculationData.Length + 2;//完整报文长度
            byte[] completeModbusMessage = new byte[messageLenth];
            for (int i = 0; i < CalculationData.Length; i++)
            {
                completeModbusMessage[i] = CalculationData[i];
            }
            completeModbusMessage[messageLenth - 2] = crc_Byte[0];
            completeModbusMessage[messageLenth - 1] = crc_Byte[1];

            //发送报文
            /*串口发送*/
            
                serialPort1.Write(completeModbusMessage, 0, completeModbusMessage.Length);

                //等待完整报文回复
                ReceiveMessageWait(cancellationToken);

                /*去除空格*/
                string AnswerMessage = lastHexString.Replace(" ", "");

                //字节数
                string byteNum_str = AnswerMessage.Substring(4, 2);
                int byteNum_int = Convert.ToInt32(byteNum_str, 16);
                int charNum_int = Convert.ToInt32(byteNum_str, 16) * 2;

                //16进制字符串转10进制整数  

                //读取的值-全部报文
                string readVal_str = AnswerMessage.Substring(6, byteNum_int * 2);
                string coilVal_str = null;

                switch (cmd_Int)
                {
                    //功能码为0时候--读取线圈
                    case 1:
                        for (int i = 0; i < charNum_int; i++)
                        {
                            if (charNum_int - 2 - (2 * i) < 0)
                            {
                                break;
                            }
                            coilVal_str += readVal_str.Substring(charNum_int - 2 - (2 * i), 2);
                        }
                        //将十六进制字符串转换为 byte[]  
                        byte[] coilVal_bytes = Enumerable.Range(0, coilVal_str.Length / 2).Select(i => Convert.ToByte(coilVal_str.Substring(i * 2, 2), 16)).ToArray();

                        //将byte[] 转成二进制字符串
                        string coilVal_binaryStr = string.Join("", coilVal_bytes.Select(b => Convert.ToString(b, 2).PadLeft(8, '0')));

                        //建立一个数组存储线圈的值
                        int coilVal_binLen = coilVal_binaryStr.Length;
                        int[] coilVal = new int[coilVal_binLen];
                        tBox_RegDataValue.Clear();

                        //赋值到线圈数组中
                        for (int i = coilVal_binLen - 1; i > 0; i--)
                        {
                            coilVal[coilVal_binLen - 1 - i] = Convert.ToInt32(coilVal_binaryStr.Substring(i, 1));
                        }

                        //在文本框显示
                        for (int i = 0; i < registerLength_Int; i++)
                        {
                            //在文本框显示
                            //String regAddress_str = (regStartAddress_Int + i).ToString("X4");           //十进制值转换为 4位 十六进制 格式字符串
                            string regAddress_str = (regStartAddress_Int + i).ToString().PadLeft(4, '0'); //十进制值转换为 4位 十进制 格式字符串

                            //功能码  + 地址  + 值
                            tBox_RegDataValue.AppendText($"{modbusAreaCode}X {regAddress_str} = {coilVal[i]}{Environment.NewLine}");
                        }
                        break;


                    //功能码为4时候--读取寄存器
                    case 4:
                        //寄存器的数量
                        int regNum_Int = byteNum_int / 2;
                        //建立一个数组存储寄存器的值
                        int[] regVal = new int[regNum_Int];
                        tBox_RegDataValue.Clear();
                        for (int i = 0; i < regNum_Int; i++)
                        {
                            regVal[i] = Convert.ToInt32(readVal_str.Substring(i * 4, 4), 16);
                            //在文本框显示
                            // 方法2：字符串插值（更简洁）
                            string regAddress_str = (regStartAddress_Int + i).ToString("X4");
                            tBox_RegDataValue.AppendText($"{modbusAreaCode} X {regAddress_str} = {regVal[i]}{Environment.NewLine}");
                        }
                        break;
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("串口未连接", "提示");
            }

        }






        /// <summary>
        /// modbus-读取
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_ModbusRead_Click(object sender, EventArgs e) { }


        /// <summary>
        /// CRC检验码计算方法
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public static ushort CalculateModbusCrc(byte[] date)
        {
            ushort crc = 0xFFFF;//CRC寄存器初始值为0xFFFF

            for (int i = 0; i < date.Length; i++)//将数据字节与CRC寄存器的低8位进行异或(XOR)操作
            {
                crc ^= date[i];

                //位处理（对每个字节的8位）：
                //检查最低位是否为1
                //右移CRC寄存器1位
                //如果最低位是1，则与多项式0xA001（0x8005的位反转）进行异或操作
                for (int j = 0; j < 8; j++)
                {
                    bool lsb = (crc & 0x0001) != 0;
                    crc >>= 1;
                    if (lsb)
                    {
                        crc ^= 0xA001; //0xA001是0x8005的位反转
                    }
                }
            }
            return crc;
        }

        private void btn_modbusTextClear_Click(object sender, EventArgs e)
        {

        }
    }
}
